package com.naja.auth2server.common.global;

import com.naja.auth2server.common.exception.BusinessException;
import com.naja.auth2server.common.dto.Result;
import com.naja.auth2server.common.validator.BeanValidators;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @desc 全局统一异常处理，包括Controller和service
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionAdvice extends ResponseEntityExceptionHandler {
    /**
     * 全局统一异常处理
     *
     * @param request
     * @param response
     * @param exception
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public Result exceptionHandler(HttpServletRequest request, HttpServletResponse response,
                                   Exception exception) {
        log.error("捕获Exception异常", exception);
        return Result.fail(exception);
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(value = {BusinessException.class})
    @ResponseBody
    public Result handleRuntimeException(HttpServletRequest request, RuntimeException ex) {
        log.error("捕获BusinessException异常", ex);
        return Result.fail(ex);
    }


    /**
     * 控制层参数校验不合法异常
     *
     * @param ex
     * @param headers
     * @param status
     * @param request
     * @return
     */
    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        BindingResult result = ex.getBindingResult();
        Map<String, String> errorMap = new HashMap<>();
        List<FieldError> errors = result.getFieldErrors();
        for (FieldError fieldError : errors) {
            errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
        }
//        Object[] errMsg = getInvalidErrMsg(errorMap);
        Result<Map<String, String>> fail = Result.fail(HttpStatus.BAD_REQUEST.value(), "参数错误", errorMap);
        log.error("捕获MethodArgumentNotValidException异常[{}]", fail);
        return new ResponseEntity<Object>(fail, status);
    }

    /**
     * 处理IConstraintViolationException异常. 系手动抛出参数不合法异常
     *
     * @param request HttpServletRequest
     * @param ex      ConstraintViolationException
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    @ExceptionHandler(value = {ConstraintViolationException.class})
    public final Result handleException(HttpServletRequest request, ConstraintViolationException ex) {
        Map<String, String> errors = BeanValidators.extractPropertyAndMessage(ex.getConstraintViolations());
//        Object[] errMsg = getInvalidErrMsg(errors);
        Result<Map<String, String>> fail = Result.fail(HttpStatus.BAD_REQUEST.value(), "参数错误", errors);
        log.error("捕获ConstraintViolationException异常[{}]", fail);
        return fail;
    }

    private Object[] getInvalidErrMsg(Map<String, String> errors) {
        Object[] errMsg = {"参考data", ""};
        if (errors != null && !errors.isEmpty()) {
            Object[] errorArr = errors.values().toArray();
            for (int i = 0; i < 2 && i < errorArr.length; i++) {
                errMsg[i] = errorArr[i];
            }
        }
        return errMsg;
    }

    /**
     * 打印FrameworkServlet捕获而未打印的org.springframework异常
     */
    @Override
    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        log.error("被FrameworkServlet捕获而未打印的org.springframework异常", ex);
        return super.handleExceptionInternal(ex, body, headers, status, request);
    }
}
